#!/usr/bin/python3
# -*- coding: utf-8 -*-

import time
import sys
import datetime
import threading
import json
import xml2dict

from signal_gen import SignalGenerator
from database_setting import DatabaseSetting
from data_agent import DataAgent


# Simulator is a runnable thread to update signals' values to DB periodically
class SignalSimulator():
    def __init__(self):
        self.dbs = None     # DatabaseSetting
        self.da = None      # DataAgent
        self.gen = None
        self.run_flag = False
        self.thd = None

        # default time slice 15 milliseconds
        self.time_slice = 15

    def parse_xml(self, filename: str):
        attrs = None

        with open(filename, 'r') as f:
            settings = f.read()
            attrs = xml2dict.parse(settings)
#           print('\nSignalSimulator dump:')
#           print(json.dumps(attrs, indent=4))
            attrs = attrs['SignalSimulator']

        if 'time_slice' in attrs:
            self.time_slice = attrs['time_slice']
        elif '@time_slice' in attrs:
            self.time_slice = attrs['@time_slice']
        else:
            print('No time slice setting, error!')
            return

        self.time_slice = int(self.time_slice)
        print('time_slice:', self.time_slice)

        if 'Signal' in attrs:
            self.parse_signal(attrs['Signal'])

        if 'DatabaseSetting' in attrs:
            db_attrs = attrs['DatabaseSetting']
            self.dbs = DatabaseSetting()
            self.dbs.parse_db(db_attrs)
            # connect DB
            self.da = DataAgent(self.dbs.ip, self.dbs.port)

        if 'SignalGenerator' in attrs:
            gen_attrs = attrs['SignalGenerator']
            signal_attrs = gen_attrs['Signal']
            self.gen = SignalGenerator()
            self.gen.parse_signal(signal_attrs)
            self.gen.set_data_agent(self.da)

    def query(self, name: str):
        t = datetime.datetime.now()
        elapsed = t - self.start_t
        return self.ss.query(name, elapsed)

    def update(ss_obj):
        print('Start simulator thread with time slice', ss_obj.time_slice)
        sleep_t = 0.001 * ss_obj.time_slice

        now = datetime.datetime.now()
        ts = now.timestamp()

        # record start time point
        start_t = now
        start_ts = ts

        # updating loop
        while ss_obj.run_flag:
            elapsed = int((ts - start_ts) * 1000)
#           print(now, 'update signals ...', elapsed)

            # update all signals
            value_dict = ss_obj.gen.query_all(elapsed)

            # write all signals to DB
            ss_obj.da.remote_write_many(value_dict)

            time.sleep(sleep_t)

            # update time
            now = datetime.datetime.now()
            ts = now.timestamp()

    def start(self, t=datetime.datetime.now()):
        # launch updating thread
        self.run_flag = True
        self.thd = threading.Thread(target=SignalSimulator.update,
                                    args=(self,))
        self.thd.start()

    def stop(self):
        self.run_flag = False
        self.thd.join()

    def suspend(self):
        pass

    def resume(self):
        pass


if __name__ == '__main__':
    if len(sys.argv) > 1:
        filename = sys.argv[1]
    else:
        filename = 'ss.xml'

    ss = SignalSimulator()
    ss.parse_xml(filename)

    # SignalGenerator test
    # > analog test
#   print(ss.gen.query('analog', 0))
#   print(ss.gen.query('analog', 250))
#   print(ss.gen.query('analog', 499))
#   print(ss.gen.query('analog', 500))
#   print(ss.gen.query('analog', 750))
#   print(ss.gen.query('analog', 999))

    # > digital test
#   print(ss.gen.query('digital', 0))
#   print(ss.gen.query('digital', 250))
#   print(ss.gen.query('digital', 500))
#   print(ss.gen.query('digital', 750))
#   print(ss.gen.query('digital', 999))

    # SignalSimulator test
    # > thread test
    ss.start()
    while True:
        cmd = input('Input "exit" to quit: ')
        if cmd == 'exit':
            break
    ss.stop()
